/**
 * Package:com.goldcitynet.util;
 * $Id: FileUtil.java,v 1.9 2009/05/15 01:22:32 xuh Exp $
 * Copyright(c) 2001-2005 www.afteryuan.com
 */
package com.afteryuan.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.security.MessageDigest;

/**
 * FileUtil,文件操作工具
 * <p/>
 * <p><a href="FileUtil.java.html"><i>View Source</i></a></p>
 *
 * @author <a href="mailto:afteryuan@afteryuan.com">$Author: xuh $</a>
 * @version $Revision: 1.9 $
 */
public class FileUtil {

    private static final Log log = LogFactory.getLog(FileUtil.class);

    /**
     * 创建任意深度的文件所在文件夹,可以用来替代直接new File(path)。
     *
     * @param path
     * @return File对象
     */
    @SuppressWarnings({"ResultOfMethodCallIgnored"})
    public static File createFile(String path) {
        File file = new File(path);
        //寻找父目录是否存在
        File parent = new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(File.separator)));
        //如果父目录不存在，则递归寻找更上一层目录
        if (!parent.exists()) {
            createFile(parent.getPath());
            //创建父目录
            parent.mkdirs();
        }
        return file;
    }
    /**
     * 创建任意深度的文件所在文件夹,可以用来替代直接new File(path)。
     *
     * @param file
     * @return File对象
     */
    @SuppressWarnings({"ResultOfMethodCallIgnored"})
    public static File createFile(File file) {
        //寻找父目录是否存在
        File parent = new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(File.separator)));
        //如果父目录不存在，则递归寻找更上一层目录
        if (!parent.exists()) {
            createFile(parent.getPath());
            //创建父目录
            parent.mkdirs();
        }
        return file;
    }

    /**
     * 用标准的ZIP算法，压缩数据并写入文件
     *
     * @param source
     * @param dest
     * @throws IOException
     */
    public static void writeCompressFile(String source, String dest) throws IOException {
        writeCompressFile(fileRead(source), dest);
    }

    /**
     * 用标准的ZIP算法，压缩数据并写入文件
     *
     * @param data
     * @param dest
     * @throws IOException
     */
    public static void writeCompressFile(byte[] data, String dest) throws IOException {
        byte[] b = CompressUtil.compressBytes(data);
        File f = createFile(dest);
        FileOutputStream fos = new FileOutputStream(f);
        fos.write(b);
        fos.close();
    }

    public static void writeUncompressFile(String srcPath, String destPath) throws IOException {
        writeUncompressFile(fileRead(srcPath), destPath);
    }

    private static void writeUncompressFile(byte[] data, String destPath) throws IOException {
        byte[] b = CompressUtil.decompressBytes(data);
        File f = createFile(destPath);
        FileOutputStream fos = new FileOutputStream(f);
        fos.write(b);
        fos.close();
    }

    /**
     * 复制文件
     *
     * @param source
     * @param dest
     * @throws IOException
     */
    public static void copyFile(File source, File dest) throws IOException {
        FileOutputStream fos = new FileOutputStream(dest);
        FileInputStream fis = new FileInputStream(source);
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) > 0) {
            fos.write(buffer, 0, len);
        }
        fos.close();
        fis.close();
    }

    /**
     * 写入文件,并把进度写入进度对象
     *
     * @param source
     * @param dest
     * @param schedule
     * @throws IOException
     */
    public static void writeFile(File source, File dest, Schedule schedule) throws IOException {
        FileOutputStream fos = new FileOutputStream(dest);
        FileInputStream fis = new FileInputStream(source);
        byte[] buffer = new byte[1024];
        int len;

        while ((len = fis.read(buffer)) > 0) {
            fos.write(buffer, 0, len);
            schedule.setDoneSize(schedule.getDoneSize() + len);
        }
        fos.close();
        fis.close();
    }

    /**
     * 读取properties文件
     *
     * @param filePath
     * @return Properties对象
     */
    public static Properties read(String filePath) {
        Properties property = new Properties();
        InputStream inStream = null;
        URL url = new FileUtil().findURL(filePath);
        if (url == null) throw new NullPointerException("指定的文件找不到！请检查路径。");
        try {
            inStream = url.openStream();
            property.load(inStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (Exception ignore) {
                }
            }
        }
        return property;
    }

    /**
     * 写入参数配置
     */
    public static boolean writeProperties(Properties property, String filePath) {
        boolean result = true;
        try {
            property.store(new FileOutputStream(filePath), "System Config Properties");
        } catch (Exception ex) {
            ex.printStackTrace();
            result = false;
        }
        return result;
    }

    /**
     * 读入文件到安符串中，本方法是以流的方式来读取。以系统默认的encoding到String<br>
     *
     * @param fileFullName 文件名，包括路径
     * @return 文件内容
     * @throws IOException 读取例外。
     */
    @SuppressWarnings({"ResultOfMethodCallIgnored"})
    public static byte[] fileRead(String fileFullName) throws IOException {
        // ---------------------------------
        // 定义返回结果变量
        // ---------------------------------
        InputStream in = null;
        byte[] bytes = new byte[0];
        try {
            File file = new File(fileFullName);
            long len = file.length();
            if (len > 0) {
                // ---------------------------------
                // 如果文件的字节数大于0，打开流
                // ---------------------------------
                in = new FileInputStream(file);
                bytes = new byte[(int) len];
                // ---------------------------------
                // 读入全部内容到byte数组中
                // ---------------------------------
                in.read(bytes);
            }
        } finally {
            if (in != null) {
                // ---------------------------------
                // 如果流不为空，则最后要关闭流。
                // ---------------------------------
                try {
                    in.close();
                    in = null;
                } catch (IOException e) {
                    // ---------------------------------
                    // 该例外不需要处理。
                    // ---------------------------------
                }
            }
        }
        return bytes;
    }

    /**
     * 在classpath下寻找名为fileName的文件，并返回其地址
     *
     * @param fileName 文件名
     * @return java.net.URL
     */
    public URL findURL(String fileName) {
        URL settingsUrl = Thread.currentThread().getContextClassLoader().getResource(fileName);
        if (settingsUrl == null) settingsUrl = getClass().getClassLoader().getResource(fileName);
        if (settingsUrl == null) {
            settingsUrl = ClassLoader.getSystemClassLoader().getResource(fileName);
        }

        log.debug("load file:" + settingsUrl);
        if (settingsUrl == null) {
            log.error(fileName + " is not exist in class path!");
        }
        return settingsUrl;
    }

    /**
     * 读取xmlm配置文件返回
     *
     * @param xmlShortName xml文件名
     * @return org.dom4j.Docuent
     */
    public Document readFile(String xmlShortName) {
        URL settingsUrl = findURL(xmlShortName);
        try {
            return new SAXReader().read(settingsUrl);
        } catch (DocumentException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取文件夹下所有一级文件和文件夹名
     *
     * @param file 文件夹路径
     * @return String[] 文件或文件夹名数组
     */
    public static String[] getFileList(File file) {
        String[] files = null;
        if (file.isDirectory()) {
            files = file.list();
        }
        return files;
    }

    /**
     * 根据文件路径返回文件所在的上一层文件夹的路径
     *
     * @param filePath
     * @return
     */
    public static String lastUpperPath(String filePath) {
        return filePath.substring(0, filePath.lastIndexOf("/"));
    }

    /**
     * 重命名文件,采用uuid的算法
     *
     * @param originalName 原始文件名
     * @return 新文件名
     */
    public static String uuidFileName(String originalName) {
        return UUID.randomUUID() + originalName.substring(originalName.lastIndexOf("."), originalName.length());
    }

    /**
     * 根据路径解析文件名,采用系统默认的文件分隔符切割
     *
     * @param path 待解析的路径
     * @return
     */
    public static String parseFileNameFromPath(String path) {
        return path.substring(path.lastIndexOf(File.separator) + 1, path.length());
    }

    /**
     * 根据文件名，解析文件扩展名
     *
     * @param name 待解析的路径
     * @return
     */
    public static String parseExtendName(String name) {
        return name.substring(name.lastIndexOf(".") + 1, name.length());
    }

    /**
     * 根据文件路径复制到相同目录，返回新文件路径
     *
     * @return String, String oldDir, String newDir
     */
    public static String copyFileFromPath(String oldPath, String newPath) throws IOException {
        //	String newPath = "copy_" + oldPath;
        File newFile = new File(newPath);
        boolean c = newFile.createNewFile();
        System.out.println(c);
        File oldFile = new File(oldPath);
        copyFile(oldFile, newFile);
        return newPath;
    }

    /**
     * 判断文件是否存在
     *
     * @param filePath
     * @return
     */
    public static boolean isFileExist(String filePath) {
        File file = new File(filePath);
        return file.exists();
    }

    /**
     * @param file   读入文件
     * @param toFile 输出文件
     * @param width  输出控制宽
     * @param height 输出控制长
     * @param suffix 后缀名
     * @throws IOException
     */
    public static void scaleImage(File file, File toFile, int width, int height, String suffix) throws IOException {
        if (suffix == null) suffix = "JPEG";
        //读为bufferedImage 以便取得参数
        java.awt.image.BufferedImage bi = ImageIO.read(file);
        //计算成为合适参数
        bi.getColorModel();
        double originalW = (double) bi.getWidth();
        double originalH = (double) bi.getHeight();
        double originalRatio = originalW / originalH;
        if (originalRatio > 1) height = (int) (height * originalRatio);
        else if (originalRatio < 1) width = (int) (width * originalRatio);
        //AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(wRatio, hRatio), null);
        //创建输出图片
        int type = bi.getType();
        BufferedImage out;
        if (type == BufferedImage.TYPE_CUSTOM) { //handmade
            ColorModel cm = bi.getColorModel();
            WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
            boolean alphaPremultiplied = cm.isAlphaPremultiplied();
            out = new BufferedImage(cm, raster, alphaPremultiplied, null);
        } else {
            out = new BufferedImage(width, height, type);
        }
        //将输入图片按照输出图片格式输出
        out.getGraphics().drawImage(bi.getScaledInstance(width, height, out.getType()), 0, 0, null);
        //将输入图片按照输出图片格式输出
        ImageIO.write(out, suffix, toFile);

    }

    /**
     * 读取绝对路径xml
     *
     * @param path 绝对路径
     * @return Document
     */
    public static Document readXml(String path) {
        File file = null;
        try {
            file = new File(path);
        } catch (Exception e) {
            log.debug(" file is not exists, please check you path");
        }
        SAXReader reader = new SAXReader();
        Document doc = null;
        try {
            doc = reader.read(file);
        } catch (DocumentException e) {
            log.debug("");
        }
        return doc;
    }

    /**
     * 列出文件夹及其子文件夹下所有符合条件的文件
     * @param filePath
     * @param fileVec
     * @param fileFilter
     * @return
     */
    public static List<File> listAllFile(String filePath, List<File> fileVec,FileFilter fileFilter) {
        File tempdir = new File(filePath);
        File[] filelist = tempdir.listFiles();
        for (int i = 0; i < filelist.length; i++) {
            File fileP = filelist[i];
            //System.out.println(file);
            if (fileP.isFile()&&fileFilter.accept(fileP)) {
                fileVec.add(fileP);
            } else if(fileP.isDirectory()) {
                listAllFile(fileP.getPath(), fileVec,fileFilter);
            }
        }
        //System.out.println("fileDir" + fileVec.toString());
        return fileVec;
    }

    /**
     * Determine whether a file or directory is actually a symbolic link.
     *
     * @param file the file or directory to check
     * @return true if so
     */
    public static boolean isLink(final File file) {
        try {
            String os = System.getProperty("os.name");
            if (os.indexOf("Windows") >= 0) {
                return false;
            }
            if (file == null || !file.exists()) {
                return false;
            } else {
                String cnnpath = file.getCanonicalPath();
                String abspath = file.getAbsolutePath();
                log.debug("comparing " + cnnpath + " and " + abspath);
                return !abspath.equals(cnnpath);
            }
        }
        catch (IOException e) {
            log.warn("could not determine whether " + file.getAbsolutePath() + " is a symbolic link", e);
            return false;
        }
    }

    /**
     * Recursively remove a directory.
     *
     * @param sourceDir the Directory to be removed
     *
     * @return true on success, false otherwise.
     *         <p>
     */
    public static boolean removeDir(final File sourceDir) {
        // try {
        // org.apache.commons.io.FileUtils.deleteDirectory(sourceDir);
        // } catch (IOException e) {
        // log.warn("could not delete " + sourceDir, e);
        // return false;
        // }
        // log.debug("Succesfully removed directory: " + sourceDir);
        // return true;

        if (sourceDir == null) {
            return false;
        }

        boolean allsuccess = true;
        boolean success = true;
        int nrOfFilesDeleted = 0;
        int nrOfDirsDeleted = 0;

        if (sourceDir.isDirectory()) {
            File[] files = sourceDir.listFiles();

            // I've seen listFiles return null, so be carefull, guess dir names too long for OS
            if (files == null) {
                log.warn("Something funny with '" + sourceDir + "'. Name or path too long?");
                log.warn("Could not delete '" + sourceDir + "' from cache");

                // see whether we can rename the dir
                if (sourceDir.renameTo(new File(sourceDir.getParent(), "1"))) {
                    log.warn("Renamed '" + sourceDir + "'");

                    return removeDir(sourceDir); // try again
                } else {
                    log.warn("Could not rename '" + sourceDir + "' to '" + sourceDir.getParent() + "1'");
                }

                return false;
            }

            log.debug(sourceDir + ": is a directory with " + files.length + " docs");

            for (int i = 0; i < files.length; i++) {
                log.debug("removing " + files[i]);

                if (files[i].isDirectory()) {
                    success = removeDir(files[i]);
                } else {
                    success = files[i].delete();
                }

                if (!success) {
                    log.warn("could not delete " + files[i] + " from cache");
                } else {
                    nrOfFilesDeleted++;
                }

                allsuccess = allsuccess && success;
            }

            log.debug("removing " + sourceDir);
            success = sourceDir.delete();

            if (!success) {
                log.warn("could not delete " + sourceDir + " from cache");
            } else {
                nrOfDirsDeleted++;
            }

            allsuccess = allsuccess && success;
        }

        // TODO: make this info at outer level of recursion
        log.debug("Deleted: " + nrOfDirsDeleted + " directories and " + nrOfFilesDeleted + " files from " + sourceDir);
        log.debug("Exiting removeDir for: " + sourceDir + ", " + allsuccess);

        return allsuccess;
    }

    /**
     * Determine whether File is somewhere within Directory.
     *
     * @param file the File.
     * @param dir the Directory.
     *
     * @return true, if so.
     */
    public static boolean isIn(final File file, final File dir) {
        if ((file == null) || !file.isFile()) {
            return false;
        }

        if ((dir == null) || !dir.isDirectory()) {
            return false;
        }

        String fileString;
        String directoryString;

        try {
            directoryString = dir.getCanonicalPath();
            fileString = file.getCanonicalPath();

            return fileString.startsWith(directoryString);
        }
        catch (IOException e) {
            log.error("Can't determine whether file is in Dir", e);
        }

        return false;
    }

    /**
     * Get the casesensitive extension (without the '.') of a file.
     *
     * @param sourceFile the File the extension is extracted from.
     *
     * @return extension, empty string if no extension.
     */
    public static String getExtension(final File sourceFile) {
        if (sourceFile == null) {
            return "";
        }

        // get the extension of the source file
        int index = sourceFile.getName().lastIndexOf('.');

        if (index != -1) {
            return sourceFile.getName().substring(index + 1);
        }

        return "";
    }

    /**
     * Create a new directory in the given directory, with prefix and postfix.
     *
     * @param sourceFile the sourceFile to use for the new directory
     * @param dir the (existing) directory to create the directory in.
     *
     * @return newly created Directory or null.
     * @throws IOException directory can't be created
     */
    public static File createTempDir(final File sourceFile, final File dir) throws IOException {
        File unZipDestinationDirectory = null;

        try {
            // get the full path (not just the name, since we could have recursed into newly created directory)
            String destinationDirectory = sourceFile.getCanonicalPath();

            log.debug("destinationDirectory: " + destinationDirectory);

            // change extension into _
            int index = destinationDirectory.lastIndexOf('.');
            String extension;

            if (index != -1) {
                extension = destinationDirectory.substring(index + 1);
                destinationDirectory = destinationDirectory.substring(0, index) + '_' + extension;
            }

            // actually create the directory
            unZipDestinationDirectory = new File(destinationDirectory);
            boolean canCreate = unZipDestinationDirectory.mkdirs();

            if (!canCreate) {
                log.warn("Could not create: " + unZipDestinationDirectory);
            }

            log.debug("Created: " + unZipDestinationDirectory + " from File: " + sourceFile);
        }
        catch (Exception e) {
            log.error("error creating directory from file: " + sourceFile, e);
        }

        return unZipDestinationDirectory;
    }

    /**
     * Get the casesensitive basename (without the '.') of a file.
     *
     * @param sourceFile the File the basename is extracted from.
     *
     * @return basename, entire name if no extension.
     */
    public static String getBasename(final File sourceFile) {
        if (sourceFile == null) {
            return "";
        }

        // get the basename of the source file
        int index = sourceFile.getName().lastIndexOf('.');

        if (index != -1) {
            return sourceFile.getName().substring(0, index);
        }

        return sourceFile.getName();
    }

    /**
     * Get the MD5 hash (unique identifier based on contents) of a file.
     *
     * <p>
     * N.B. This is an expensive operation, since the entire file is read.
     * </p>
     *
     * @param sourceFile the File the MD5 hash is created from, can take null or not a normalFile
     *
     * @return MD5 hash of file as a String, null if it can't create a hash.
     */
    public static String getMD5Hash(final File sourceFile) {
        log.debug("Getting MD5 hash for " + sourceFile);

        final char[] HEX = "0123456789abcdef".toCharArray();

        if (sourceFile == null || !sourceFile.isFile()) {
            log.error("Error creating MD5 Hash for " + sourceFile);
            return null;
        }
        BufferedInputStream bis = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            // IMessageDigest md = HashFactory.getInstance("MD5");
            if (md == null) {
                log.error("Error creating MessageDigest for " + sourceFile);
                return null;
            }

            bis = new BufferedInputStream(new FileInputStream(sourceFile));
            md.reset();
            int len = 0;
            byte[] buffer = new byte[8192];
            while ((len = bis.read(buffer)) > -1) {
                md.update(buffer, 0, len);
            }

            byte[] bytes = md.digest();
            if (bytes == null) {
                log.error("MessageDigest has no bytes for " + sourceFile);

                return null;
            }

            // base64? encode the digest
            StringBuffer sb = new StringBuffer(bytes.length * 2);
            int b;
            for (int i = 0; i < bytes.length; i++) {
                b = bytes[i] & 0xFF;
                sb.append(HEX[b >>> 4]);
                sb.append(HEX[b & 0x0F]);
            }

            log.debug("MD5 hash for " + sourceFile + " is " + sb);
            return sb.toString();
        }
        catch (Exception e) {
            log.error("Can't determine MD5 hash for " + sourceFile, e);

            return null;
        }
        finally {
            if (bis != null) {
                try {
                    bis.close();
                }
                catch (IOException e) {
                    log.warn("Can't close stream for " + sourceFile, e);
                }
            }
        }
    }
    public static void main(String[] args) throws IOException {
//        File file = new File("e:\\testPic\\topP.png");
//        File toFile = new File("e:\\testPic\\topP1.png");
//        int width = 80;
//        int height = 80;
//        FileUtil.scaleImage(file, toFile, width, height, "png");
        List<File> files = new ArrayList<File>();
        FileUtil.listAllFile("E:\\Novel",files,new SuffixFileFilter(".txt"));
        System.out.println("files:"+files.size());
        for (File file : files) {
            System.out.println("file:"+file.getName());
            System.out.println("file:"+file.getName().substring(0,file.getName().indexOf(".")));
        }
    }


}
